import 'dart:async';
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
import 'package:flutter_mvvm/service/rxNet/dbUtil.dart';
import 'package:flutter_mvvm/service/rxNet/stringUtil.dart';
import 'NetUtil.dart';

typedef JsonTransformation<T> = T Function(String);

class RxDio<T> {
  String _baseurl;
  String _path;
  REQUEST_METHORD _requestMethord;
  CacheMode _cacheMode;
  Map<String, String> _params;
  JsonTransformation<T> _jsonTransformation = (data) {
    return data as T;
  };

  static Future initDatabase() {
    return DatabaseUtil.initDatabase();
  }

  static bool isDatabaseReady() {
    return DatabaseUtil.isDatabaseReady;
  }

  RxDio() : super();

  void setBaseUrl(String url) {
    this._baseurl = url;
  }

  void setPath(String path) {
    if (StringUtil.isEmpty(path)) {
      throw new Exception("path can not be null!");
      return;
    }
    this._path = path;
  }

  void setMethord(REQUEST_METHORD requestMethord) {
    this._requestMethord = requestMethord;
  }

  void setParams(Map<String, String> params) {
    this._params = params;
  }

  void setCacheMode(CacheMode cacheMode) {
    this._cacheMode = cacheMode;
  }

  void setJsonTransFrom(JsonTransformation<T> jsonTransformation) {
    _jsonTransformation = jsonTransformation;
  }

  void call(NetCallback<T> netCallback) {
    // ignore: close_sinks

    // ignore: close_sinks
    StreamController<RequestData<T>> controller =
        new StreamController<RequestData<T>>();

    controller.stream.listen((requestData) {
      if (requestData.requestType == RequestType.NET) {
        netCallback.onNetFinish(requestData.data);
      } else if (requestData.requestType == RequestType.CACHE) {
        netCallback.onCacheFinish(requestData.data);
      } else {
        netCallback.onUnkownFinish(requestData.data);
      }
    });
    initDatabase().then((s) {
      //不走缓存，只走网络
      if (_cacheMode == CacheMode.NO_CACHE) {
        if (_requestMethord == REQUEST_METHORD.GET) {
          Future<Response<String>> future =
              NetUtils.getNet<String>(_baseurl + _path, _params);

          future.then((response) {
            controller.add(new RequestData(
                requestType: RequestType.NET,
                data: _jsonTransformation(response.data)));
          });
        } else if (_requestMethord == REQUEST_METHORD.POST) {
          Future<Response<String>> future =
              NetUtils.postNet<String>(_baseurl + _path, _params);
          future.then((response) {
            controller.add(new RequestData(
                requestType: RequestType.NET,
                data: _jsonTransformation(response.data)));
          });
        }
      }
      //先获取缓存，在获取网络
      else if (_cacheMode == CacheMode.FIRST_CACHE_THEN_REQUEST) {
        NetUtils.getCache(_baseurl + _path, _params).then((list) {
          if (list.length > 0) {
            controller.add(new RequestData(
                requestType: RequestType.CACHE,
                data:
                    _jsonTransformation(json.decoder.convert(list[0]["value"])),
                statusCode: 200));
          } else {
            controller.add(new RequestData(
                requestType: RequestType.CACHE, data: null, statusCode: 400));
          }
        });

        if (_requestMethord == REQUEST_METHORD.GET) {
          NetUtils.getNet<String>(_baseurl + _path, _params).then((response) {
            controller.add(new RequestData(
                requestType: RequestType.NET,
                data: _jsonTransformation(response.data)));
            NetUtils.saveCache(
                NetUtils.getCacheKeyFromPath(_baseurl + _path, _params),
                json.encoder.convert(response.data));
          });
        } else if (_requestMethord == REQUEST_METHORD.POST) {
          NetUtils.postNet<String>(_baseurl + _path, _params).then((response) {
            controller.add(new RequestData(
                requestType: RequestType.NET,
                data: _jsonTransformation(response.data)));
            NetUtils.saveCache(
                NetUtils.getCacheKeyFromPath(_baseurl + _path, _params),
                json.encoder.convert(response.data));
          });
        }
      }
      //先请求网络，如果请求网络失败，则读取缓存，如果读取缓存失败，本次请求失败
      else if (_cacheMode == CacheMode.REQUEST_FAILED_READ_CACHE) {
        if (_requestMethord == REQUEST_METHORD.GET) {
          NetUtils.getNet<String>(_baseurl + _path, _params).then((response) {
            if (response.statusCode == 200) {
              controller.add(new RequestData(
                  requestType: RequestType.NET,
                  data: _jsonTransformation(response.data)));
            } else {
              NetUtils.getCache(_baseurl + _path, _params).then((list) {
                if (list.length > 0) {
                  controller.add(new RequestData(
                      requestType: RequestType.CACHE,
                      data: _jsonTransformation(
                          json.decoder.convert(list[0]["value"])),
                      statusCode: 200));
                } else {
                  controller.add(new RequestData(
                      requestType: RequestType.CACHE,
                      data: null,
                      statusCode: 400));
                }
              });
            }
          });
        } else if (_requestMethord == REQUEST_METHORD.POST) {
          NetUtils.postNet<String>(_baseurl + _path, _params).then((response) {
            if (response.statusCode == 200) {
              controller.add(new RequestData(
                  requestType: RequestType.NET,
                  data: _jsonTransformation(response.data)));
            } else {
              NetUtils.getCache(_baseurl + _path, _params).then((list) {
                if (list.length > 0) {
                  controller.add(new RequestData(
                      requestType: RequestType.CACHE,
                      data: _jsonTransformation(
                          json.decoder.convert(list[0]["value"])),
                      statusCode: 200));
                } else {
                  controller.add(new RequestData(
                      requestType: RequestType.CACHE,
                      data: null,
                      statusCode: 400));
                }
              });
            }
          });
        }
      }
    });
  }
}

typedef NetCallbackFunction<T> = void Function(T);

class NetCallback<T> {
  NetCallbackFunction<T> onNetFinish;
  NetCallbackFunction<T> onCacheFinish;
  NetCallbackFunction<T> onUnkownFinish;

  NetCallback({this.onCacheFinish, this.onNetFinish, this.onUnkownFinish});
}

class RequestData<T> {
  RequestType requestType;
  int statusCode;
  T data;

//  RequestData(this.requestType, this.data);

  RequestData(
      {@required this.requestType, @required this.data, this.statusCode});
}

enum RequestType {
  NET,
  CACHE,
  UNKOWN,
}
enum CacheMode {
  NO_CACHE, //没有缓存
  REQUEST_FAILED_READ_CACHE, //先请求网络，如果请求网络失败，则读取缓存，如果读取缓存失败，本次请求失败
  FIRST_CACHE_THEN_REQUEST, //先使用缓存，不管是否存在，仍然请求网络
}
enum REQUEST_METHORD {
  GET,
  POST,
}
